package watcher

import (
	"context"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/fsnotify/fsnotify"
)

type EventHandler func(path string, op fsnotify.Op)

type Watcher struct {
	watcher        *fsnotify.Watcher
	workDir        string
	excludes       []string
	eventHandler   EventHandler
	debounceTimers map[string]*time.Timer
	debounceMu     sync.Mutex
	debounceDelay  time.Duration
	ctx            context.Context
	cancel         context.CancelFunc
	done           chan struct{}
	wg             sync.WaitGroup
	paused         bool
	pauseMu        sync.Mutex
}

func New(workDir string) (*Watcher, error) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}

	ctx, cancel := context.WithCancel(context.Background())

	return &Watcher{
		watcher:        watcher,
		workDir:        workDir,
		debounceTimers: make(map[string]*time.Timer),
		debounceDelay:  50 * time.Millisecond, // Default debounce delay
		ctx:            ctx,
		cancel:         cancel,
		done:           make(chan struct{}),
	}, nil
}

func (w *Watcher) SetEventHandler(handler EventHandler) {
	w.eventHandler = handler
}

// Exclude 添加要排除的文件模式
func (w *Watcher) Exclude(patterns ...string) {
	w.excludes = append(w.excludes, patterns...)
}

// isExcluded 检查文件是否在排除列表中
func (w *Watcher) isExcluded(path string) bool {
	for _, pattern := range w.excludes {
		matched, _ := filepath.Match(pattern, filepath.Base(path))
		if matched {
			return true
		}
	}
	return false
}

// AddRecursive adds a path and all its subdirectories to the watcher.
func (w *Watcher) AddRecursive(path string) error {
	return filepath.Walk(path, func(p string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			log.Printf("Adding directory to watch: %s", p)
			return w.watcher.Add(p)
		}
		return nil
	})
}

// Pause pauses the event handling
func (w *Watcher) Pause() {
	w.pauseMu.Lock()
	defer w.pauseMu.Unlock()
	w.paused = true
	log.Printf("Watcher paused for directory: %s", w.workDir)
}

// Resume resumes the event handling
func (w *Watcher) Resume() {
	w.pauseMu.Lock()
	defer w.pauseMu.Unlock()
	w.paused = false
	log.Printf("Watcher resumed for directory: %s", w.workDir)
}

func (w *Watcher) Start() error {
	w.wg.Add(1)
	go func() {
		defer w.wg.Done()
		// 递归添加监控目录
		if err := w.AddRecursive(w.workDir); err != nil {
			log.Printf("Error adding directories recursively: %v", err)
		}
	}()

	log.Printf("Watching directory recursively: %s", w.workDir)

	w.wg.Add(1)
	go func() {
		defer w.wg.Done()
		defer close(w.done)

		for {
			select {
			case <-w.ctx.Done():
				log.Printf("Watcher context cancelled, stopping...")
				return

			case event, ok := <-w.watcher.Events:
				if !ok {
					return
				}

				if w.isExcluded(event.Name) {
					continue
				}

				w.pauseMu.Lock()
				if w.paused {
					w.pauseMu.Unlock()
					continue
				}
				w.pauseMu.Unlock()

				if w.eventHandler != nil {
					w.debounceMu.Lock()
					timer, exists := w.debounceTimers[event.Name]
					if exists {
						timer.Reset(w.debounceDelay)
					} else {
						w.debounceTimers[event.Name] = time.AfterFunc(w.debounceDelay, func() {
							// 检查context是否已取消
							select {
							case <-w.ctx.Done():
								return
							default:
								// When the timer fires, call the handler
								w.eventHandler(event.Name, event.Op)
							}

							// Clean up the timer from the map
							w.debounceMu.Lock()
							delete(w.debounceTimers, event.Name)
							w.debounceMu.Unlock()
						})
					}
					w.debounceMu.Unlock()
				}

			case err, ok := <-w.watcher.Errors:
				if !ok {
					return
				}
				log.Printf("Watcher error: %v", err)
			}
		}
	}()

	return nil
}

func (w *Watcher) Close() error {
	// 取消context以停止所有goroutine
	w.cancel()

	// 清理所有待处理的timer
	w.debounceMu.Lock()
	for path, timer := range w.debounceTimers {
		timer.Stop()
		delete(w.debounceTimers, path)
	}
	w.debounceMu.Unlock()

	// 等待所有goroutine完成
	w.wg.Wait()

	// 关闭文件监控器
	return w.watcher.Close()
}
